在本系列文中,所有的程式碼以及測試都可以在 should-i-use-fp-ts 找到,今日的範例放在 src/day-06
並且有習題和測試可以讓大家練習。
接下來要開始接觸 fp-ts
之中常常使用到的工具了 pipe
, flow
,這邊繼續使用昨天的函式以及變數來進行解說。
昨天的 function 最後是這個形式。
list = [1, 2, 3, 4, 5];
const inc = (x: number): number => x + 1;
const toString = (x: number): string => x.toString();
// point
list.map(x => inc(x))
.map(x => toString(x));
// pointfree
list.map(inc)
.map(toString);
這邊的函式重複使用了 map
兩次,但這邊其實只要一次 map
即可完成邏輯。
// point
list.map(x => toString(inc(x)));
// pointfree error
list.map(toString(inc)); // Error
這樣的話只要一次迴圈就可以處理完畢,但這邊無法使用 pointfree
,因為這樣子寫並無法正確傳入變數,如果要使用 pointfree
的話可能就需要先將函數 組合
起來。
const incAndToString = (x: number): string => toString(inc(x));
list.map(incAndToString);
但 incAndToString
的泛用程度明顯會比 inc
或是 toString
還要低的多,為這個處理特別產生一個函式並不是個優雅而且簡潔的寫法,如果可以製作一個專門 組合
函式的函式就可以解決這個問題。
一樣用上述的例子來製作組合函數 flow
:
const inc = (x: number): number => x + 1;
const toString = (x: number): string => x.toString();
const incAndToString = (x: number): string => toString(inc(x));
// 製作一個 flow2 來組合 inc 和 toStirng 兩個函數
// arrow function style
type Flow2 = (f: (x: number) => number, g: (x: number) => string)
=> (x: number) => string
const flow2: Flow2 = (f, g) => x => g(f(x));
// funtion style
function flow2F(f: (x: number) => number, g: (x: number) => string) {
return function (x: number) {
return g(f(x));
};
}
flow2
先接受 f
, g
兩個函數,之後回傳一個接受 x
變數後會傳回運算結果的函數。
這邊需要注意型別的部分,在放入 x
的時候會先被 f
運算,再經由 g
運算,型別會是經由以下變化。
"input f output"
"input g output"
number => number => string
x f(x) g(f(x))
所以在組合函式的時候需要注意相鄰兩個函式的之間的型別是否相同才能組合,但剛剛做的 flow2
只能接受 number => number => string
,這樣組合的侷限性太大了,接下來以 Generic
來重新製作一個組合函式。
type Flow2 = <A, B, C>(fn1: (x: A) => B, fn2: (x: B) => C) => (x: A) => C;
const flow2: Flow2 = (fn1, fn2) => x => fn2(fn1(x));
list.map(flow2(inc, toString)); // ['2', '3', '4', '5', '6']
list.map(flow2(toString, split)); // [['1'], ['2'], ['3'], ['4'], ['5']]
如此一來就可以製作型別安全的 flow
了,最後稍微調整一下來製作 pipe
。
type Pipe2 <A, B, C>(x: A, fn1: (x: A) => B, fn2: (x: B) => C) => C;
const pipe2: Pipe2 = (x, fn1, fn2) => (fn2, fn1(x));
// usage
pipe(1, inc, toString) // '2'
今天的主題在 should-i-use-fp-ts 在 src/day-06
並且有習題和測試可以讓大家練習。
Functional Programming - 04: Function Composition